//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2020-04-20     ZhangYihua   first version
//
// Description  : 
//################################################################################

module clk_div_50pdc #(   // divide clock with 50% duty cycle
parameter           DIV_MAX                 = 3,
parameter           DIV_ODD_EN              = 1'b1,
parameter           DIV_ONE_EN              = 1'b1,

// the following parameters are calculated automatically
parameter           DIV_BW                  = $clog2(DIV_MAX+1)
) ( 
input                                       rst_n,
input                                       clk,

input               [DIV_BW-1:0]            cfg_div,    // range [1:DIV_MAX]
output                                      clk_div
);

//################################################################################
// define local varialbe and localparam
//################################################################################
localparam  [DIV_BW-1:0]    DIV_ONE         = 1;

reg                                         div_bypass;
wire                                        clk_bypass;
reg                 [DIV_BW-1:0]            cnt_div;
reg                                         clk_div_r;

//################################################################################
// main
//################################################################################

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        div_bypass <=`U_DLY 1'b0;
    end else begin
        if (cfg_div<=DIV_ONE)
            div_bypass <=`U_DLY 1'b1;
        else
            div_bypass <=`U_DLY 1'b0;
    end
end

generate if (DIV_ONE_EN==1'b1) begin:G_ONE
    assign clk_bypass = clk & div_bypass;
end else begin:G_NONE
    assign clk_bypass = 1'b0;
end endgenerate

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        cnt_div <=`U_DLY DIV_ONE;
    end else if (div_bypass==1'b0) begin
        if (cnt_div>=cfg_div)
            cnt_div <=`U_DLY DIV_ONE;
        else
            cnt_div <=`U_DLY cnt_div + 1'd1;
    end else
        ;
end

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        clk_div_r <=`U_DLY 1'b1;
    end else if (div_bypass==1'b0) begin
        if (cnt_div<={1'b0, cfg_div[1+:DIV_BW-1]})  // int(cfg_div/2)
            clk_div_r <=`U_DLY 1'b1;                // 50% for evne and less than 50% for odd
        else
            clk_div_r <=`U_DLY 1'b0;
    end else
        clk_div_r <=`U_DLY 1'b0;
end

generate if (DIV_ODD_EN==1'b1) begin:G_ODD
    reg                                         clk_div_f;

    always@(negedge clk or negedge rst_n) begin     // falling edge of clk
        if (rst_n==1'b0) begin
            clk_div_f <=`U_DLY 1'b0;
        end else if ((cfg_div[0]==1'b1) && (div_bypass==1'b0)) begin    // cfg_div is odd
            clk_div_f <=`U_DLY clk_div_r;
        end else
            clk_div_f <=`U_DLY 1'b0;
    end
    
    assign clk_div = clk_bypass | (clk_div_r | clk_div_f);
end else begin:G1_NODD
    assign clk_div = clk_bypass | clk_div_r;
end endgenerate

//################################################################################
// ASSERTION
//################################################################################

`ifdef CBB_ASSERT_ON
// synopsys translate_off


// synopsys translate_on
`endif

endmodule
